﻿using StockTradTest;
using StockTradTest.Data;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using StockTradTest.strategy;
using Index = StockTradTest.Data.Index;

using SharpDX;
using SharpDX.Direct2D1;
using SharpDX.DirectWrite;
using SharpDX.DXGI;
using System.Drawing.Imaging;
using SharpDX.RawInput;
using AlphaMode = SharpDX.Direct2D1.AlphaMode;
using Color = SharpDX.Color;
using Factory = SharpDX.Direct2D1.Factory;
using FontStyle = SharpDX.DirectWrite.FontStyle;
using TextAntialiasMode = SharpDX.Direct2D1.TextAntialiasMode;
using Bitmap = SharpDX.Direct2D1.Bitmap;
using PixelFormat = SharpDX.Direct2D1.PixelFormat;
using SharpDX.Multimedia;
using SharpDX.Mathematics.Interop;
using System.IO;
using Point = SharpDX.Point;

namespace TestGraphic
{
    public partial class Form5 : Form
    {


        double maxPrice = 0;
        double minPrice = double.MaxValue;
        //股票历史数据
        List<Index> indexData = null;
        List<Indicator> indicators = null;

        const double stockPersent = 0.45f;
        const double bollPersent = 0.23f;
        const double volumePersent = 0.2f;

        public Factory Factory2D { get; private set; }
        public SharpDX.DirectWrite.Factory FactoryDWrite { get; private set; }
        public WindowRenderTarget RenderTarget2D { get; private set; }

        public SolidColorBrush redLineBrush { get; private set; }
        public SolidColorBrush greenLineBrush { get; private set; }
        public SolidColorBrush blackLineBrush { get; private set; }
        public SolidColorBrush whiteLineBrush { get; private set; }
        public SolidColorBrush yellowLineBrush { get; private set; }
        public SolidColorBrush purpleLineBrush { get; private set; }

        public Bitmap arrowUp { get; private set; }
        public Bitmap arrowDown { get; private set; }
        TextFormat textFormat;
        bool LMouseDown = false;

        //鼠标按下时候的位置记录
        RawVector2 mouseDownPoint = new RawVector2(0, 0);
        RawVector2 mousePos;

        //显示的开始位置
        int showStartCount = 0;
        int showCount = 0;
        //记录的
        int oldShowStartCount = 0;
        //最大计数
        int maxCount = 0;

        //窗口处于处理阶段
        public static bool inFocus = true;

        //线的x坐标
        int lineX = -1;

        string showText = "";
        //指数列表
        string[] indexCodeList;
        public Form5()
        {
            InitializeComponent();
            InitComboBox();
            comboBox_code.SelectedIndex = 0;
            textBox_variable.Text = "20";
            SetWindowSize();
            InitDirect2DAndDirectWrite();

        }
        void InitComboBox()
        {
            comboBox_code.Items.AddRange(StockTradTest.Data.Index.IndexDic.Values.ToArray());
            indexCodeList = StockTradTest.Data.Index.IndexDic.Keys.ToArray();
        }

        /// <summary>
        /// Inits the direct2D and direct write.
        /// </summary>
        private void InitDirect2DAndDirectWrite()
        {
            Factory2D = new SharpDX.Direct2D1.Factory();
            FactoryDWrite = new SharpDX.DirectWrite.Factory();

            var properties = new HwndRenderTargetProperties();
            properties.Hwnd = renderControl.Handle;
            properties.PixelSize = new Size2(renderControl.ClientSize.Width, renderControl.ClientSize.Height);
            properties.PresentOptions = PresentOptions.None;

            RenderTarget2D = new WindowRenderTarget(Factory2D, new RenderTargetProperties(new PixelFormat(Format.Unknown, AlphaMode.Premultiplied)), properties);
            RenderTarget2D.AntialiasMode = AntialiasMode.PerPrimitive;
            RenderTarget2D.TextAntialiasMode = TextAntialiasMode.Cleartype;

            redLineBrush = new SolidColorBrush(RenderTarget2D, Color.Red);
            greenLineBrush = new SolidColorBrush(RenderTarget2D, Color.Green);
            blackLineBrush = new SolidColorBrush(RenderTarget2D, Color.Black);
            whiteLineBrush = new SolidColorBrush(RenderTarget2D, Color.White);
            yellowLineBrush = new SolidColorBrush(RenderTarget2D, Color.Yellow);
            purpleLineBrush = new SolidColorBrush(RenderTarget2D, Color.Purple);
            textFormat = new TextFormat(FactoryDWrite, "宋体", 15) { TextAlignment = TextAlignment.Center, ParagraphAlignment = ParagraphAlignment.Center };
            RenderTarget2D.TextAntialiasMode = TextAntialiasMode.Cleartype;
            arrowUp = LoadFromFile(RenderTarget2D, true);

            arrowDown = LoadFromFile(RenderTarget2D, false);
            renderControl.Paint += RenderControlPaint;
            renderControl.Resize += new System.EventHandler(RenderControlResize);
        }

        /// <summary>
        /// Loads a Direct2D Bitmap from a file using System.Drawing.Image.FromFile(...)
        /// </summary>
        /// <param name="renderTarget">The render target.</param>
        /// <param name="file">The file.</param>
        /// <returns>A D2D1 Bitmap</returns>
        public static Bitmap LoadFromFile(RenderTarget renderTarget, bool isUp)
        {
            System.Drawing.Bitmap bitmap = Resource1.arrow;
            if (isUp) bitmap.RotateFlip(RotateFlipType.Rotate180FlipX);
            var sourceArea = new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height);
            var bitmapProperties = new BitmapProperties(new PixelFormat(Format.R8G8B8A8_UNorm, AlphaMode.Premultiplied));
            var size = new Size2(20, 20);

            // Transform pixels from BGRA to RGBA
            int stride = bitmap.Width * sizeof(int);
            using (var tempStream = new DataStream(bitmap.Height * stride, true, true))
            {
                // Lock System.Drawing.Bitmap
                var bitmapData = bitmap.LockBits(sourceArea, ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppPArgb);

                // Convert all pixels 
                for (int y = 0; y < bitmap.Height; y++)
                {
                    int offset = bitmapData.Stride * y;
                    for (int x = 0; x < bitmap.Width; x++)
                    {
                        // Not optimized 
                        byte B = Marshal.ReadByte(bitmapData.Scan0, offset++);
                        byte G = Marshal.ReadByte(bitmapData.Scan0, offset++);
                        byte R = Marshal.ReadByte(bitmapData.Scan0, offset++);
                        byte A = Marshal.ReadByte(bitmapData.Scan0, offset++);
                        int rgba = R | (G << 8) | (B << 16) | (A << 24);
                        tempStream.Write(rgba);
                    }

                }
                bitmap.UnlockBits(bitmapData);
                tempStream.Position = 0;
                return new Bitmap(renderTarget, size, tempStream, stride, bitmapProperties);
            }
        }
        private void DrawArrow(RawVector2 point, bool isUp)
        {

            if (isUp)
            {
                point.Y += 2;
            }
            else
            {
                point.Y -= (int)(arrowUp.Size.Height * 0.2) + 2;
            }
            point.X -= (int)(arrowUp.Size.Width * 0.1);
            RenderTarget2D.DrawBitmap(isUp ? arrowUp : arrowDown, new RawRectangleF(point.X, point.Y, point.X + arrowUp.Size.Width, point.Y + arrowUp.Size.Height), 1.0f, BitmapInterpolationMode.Linear);
        }

        /// <summary>
        /// Handles the Paint event of the renderControl control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.PaintEventArgs"/> instance containing the event data.</param>
        void RenderControlPaint(object sender, PaintEventArgs e)
        {
            try
            {
                RenderTarget2D.BeginDraw();

                RenderTarget2D.Clear(Color.White);
                GraphicsClear();
                DrawAll(showStartCount);

                RenderTarget2D.DrawLine(new RawVector2((int)(mousePos.X / 5) * 5 - 1, 1), new RawVector2((int)(mousePos.X / 5) * 5 - 1, renderControl.Height), blackLineBrush, 1);
                RawRectangleF rf = new RawRectangleF(mousePos.X + 10, 10, mousePos.X + 170, 270);
                if (mousePos.X > 0.8 * renderControl.Width)
                {
                    rf = new RawRectangleF(mousePos.X - 180, 10, mousePos.X - 10, 270);
                }
                else
                {
                    rf = new RawRectangleF(mousePos.X + 10, 10, mousePos.X + 170, 270);
                }
                RenderTarget2D.DrawRectangle(rf, blackLineBrush);
                RenderTarget2D.DrawText(showText, textFormat, rf, blackLineBrush);
                RenderTarget2D.EndDraw();

            }
            catch (Exception ex)
            {

            }
        }
        /// <summary>
        /// Handles the Resize event of the renderControl control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        void RenderControlResize(object sender, System.EventArgs e)
        {
            try
            {
                RenderTarget2D.Resize(new Size2(renderControl.Size.Width, renderControl.Size.Height));
            }
            catch (Exception ex)
            {

            }
        }
        private void DrawStock(int startCount = 0)
        {
            maxPrice = 0;
            minPrice = double.MaxValue;
            double heightLength = renderControl.Height * stockPersent - 5;
            if (indexData == null) return;
            maxCount = indexData.Count;
            showCount = (int)Math.Floor((renderControl.Width) / 5f - 2);
            if (startCount + showCount >= maxCount)
            {
                startCount = maxCount - showCount;
                showStartCount = startCount;
            }
            for (int i = startCount; i < startCount + showCount; i++)
            {
                if (maxPrice < indexData[i].HighPrice) maxPrice = indexData[i].HighPrice;
                if (minPrice > indexData[i].LowPrice) minPrice = indexData[i].LowPrice;
            }
            List<Point> points = new List<Point>();
            double length = maxPrice - minPrice;

            for (int i = 0; i < showCount; i++)
            {
                RawVector2 point1 = new RawVector2(i * 5 + 4, 5 + (int)(heightLength - (indexData[i + startCount].HighPrice - minPrice) / length * heightLength));
                RawVector2 point2 = new RawVector2(i * 5 + 4, 5 + (int)(heightLength - (indexData[i + startCount].LowPrice - minPrice) / length * heightLength));
                RawVector2 point3 = new RawVector2(i * 5 + 4, 5 + (int)(heightLength - (indexData[i + startCount].OpenPrice - minPrice) / length * heightLength));
                RawVector2 point4 = new RawVector2(i * 5 + 4, 5 + (int)(heightLength - (indexData[i + startCount].ClosePrice - minPrice) / length * heightLength));
                if (indexData[i + startCount].OpenPrice < indexData[i + startCount].ClosePrice)
                {
                    RenderTarget2D.DrawLine(point1, point2, redLineBrush, 1);
                    RenderTarget2D.DrawLine(point3, point4, redLineBrush, 3);
                }
                else
                {
                    RenderTarget2D.DrawLine(point1, point2, greenLineBrush, 1);
                    RenderTarget2D.DrawLine(point3, point4, greenLineBrush, 3);
                }

            }
        }
        // 在主图上绘制Ma移动平均线
        private void DrawMa(int n, Color color)
        {
            double heightLength = renderControl.Height * stockPersent - 5;
            if (indicators == null) return;
            List<RawVector2> points = new List<RawVector2>();
            double length = maxPrice - minPrice;
            double tempMa;
            for (int i = 0; i < showCount; i++)
            {
                tempMa = n switch
                {
                    5 => indicators[i + showStartCount].Ma5,
                    10 => indicators[i + showStartCount].Ma10,
                    20 => indicators[i + showStartCount].Ma20,
                    30 => indicators[i + showStartCount].Ma60,
                    60 => indicators[i + showStartCount].Ma60,
                    120 => indicators[i + showStartCount].Ma120,
                    250 => indicators[i + showStartCount].Ma250,
                    _ => 0
                };

                points.Add(new RawVector2(i * 5 + 4, 5 + (int)(heightLength - (tempMa - minPrice) / length * heightLength)));
            }
            if (points.Count > 0)
            {
                DrawPoints(points, new SolidColorBrush(RenderTarget2D, color), 1);
            }
        }
        void DrawPoints(List<RawVector2> points, SolidColorBrush brush, float width)
        {
            for (int i = 0; i < points.Count - 1; i++)
            {
                RenderTarget2D.DrawLine(points[i], points[i + 1], brush, width);
            }

        }
        /// <summary>
        /// 绘制换手量和成交资金量
        /// </summary>
        private void DrawVolume()
        {
            double heightLength = renderControl.Height * volumePersent - 5;
            double title = renderControl.Height * (stockPersent + volumePersent) + 4;
            if (indexData == null || indexData.Count == 0) return;
            double maxHand = 0;
            double maxAmount = 0;
            for (int i = showStartCount; i < showCount + showStartCount; i++)
            {
                if (maxHand < indexData[i].TotalHand) maxHand = indexData[i].TotalHand;
                if (maxAmount < indexData[i].TotalAmount) maxAmount = indexData[i].TotalAmount;
            }
            for (int i = 0; i < showCount; i++)
            {
                RawVector2 point1 = new RawVector2(i * 5 + 3, (int)title);
                RawVector2 point2 = new RawVector2(i * 5 + 3, (int)(title - indexData[i + showStartCount].TotalHand / maxHand * heightLength));
                RawVector2 point3 = new RawVector2(i * 5 + 5, (int)title);
                RawVector2 point4 = new RawVector2(i * 5 + 5, (int)(title - indexData[i + showStartCount].TotalAmount / maxAmount * heightLength));
                RenderTarget2D.DrawLine(point3, point4, greenLineBrush, 2);
                RenderTarget2D.DrawLine(point1, point2, redLineBrush, 2);
            }
        }
        private void DrawBoll()
        {
            double heightLength = renderControl.Height * bollPersent - 5;
            double title = renderControl.Height * (stockPersent + volumePersent + bollPersent) + 5;

            if (indexData == null || indexData.Count == 0) return;
            double maxPrice = 0;
            double minPrice = double.MaxValue;
            for (int i = showStartCount; i < showCount + showStartCount; i++)
            {
                if (i == maxCount) break;
                if (maxPrice < indicators[i].BollUp) maxPrice = indicators[i].BollUp;
                if (minPrice > indicators[i].BollDown) minPrice = indicators[i].BollDown;
                if (maxPrice < indexData[i].HighPrice) maxPrice = indexData[i].HighPrice;
                if (minPrice > indexData[i].LowPrice) minPrice = indexData[i].LowPrice;
            }
            List<RawVector2> pointsUp = new List<RawVector2>();
            List<RawVector2> pointsMd = new List<RawVector2>();
            List<RawVector2> pointsDown = new List<RawVector2>();
            double length = maxPrice - minPrice;
            for (int i = 0; i < showCount; i++)
            {
                if (i == maxCount) break;
                pointsUp.Add(new RawVector2(i * 5 + 4, (int)(title - (indicators[i + showStartCount].BollUp - minPrice) / length * heightLength)));
                pointsMd.Add(new RawVector2(i * 5 + 4, (int)(title - (indicators[i + showStartCount].BollMd - minPrice) / length * heightLength)));
                pointsDown.Add(new RawVector2(i * 5 + 4, (int)(title - (indicators[i + showStartCount].BollDown - minPrice) / length * heightLength)));
            }
            if (pointsUp.Count > 0)
            {
                DrawPoints(pointsUp, whiteLineBrush, 1);
                DrawPoints(pointsMd, yellowLineBrush, 1);
                DrawPoints(pointsDown, purpleLineBrush, 1);
            }
            //Draw Stock on Indicator

            if (indexData == null) return;
            List<RawVector2> points = new List<RawVector2>();

            for (int i = 0; i < showCount; i++)
            {
                if (i == maxCount) break;
                RawVector2 point1 = new RawVector2(i * 5 + 4, (int)(title - (indexData[i + showStartCount].HighPrice - minPrice) / length * heightLength));
                RawVector2 point2 = new RawVector2(i * 5 + 4, (int)(title - (indexData[i + showStartCount].LowPrice - minPrice) / length * heightLength));
                RawVector2 point3 = new RawVector2(i * 5 + 4, (int)(title - (indexData[i + showStartCount].OpenPrice - minPrice) / length * heightLength));
                RawVector2 point4 = new RawVector2(i * 5 + 4, (int)(title - (indexData[i + showStartCount].ClosePrice - minPrice) / length * heightLength));
                if (indexData[i].OpenPrice < indexData[i].ClosePrice)
                {
                    RenderTarget2D.DrawLine(point1, point2, redLineBrush, 1);
                    RenderTarget2D.DrawLine(point3, point4, redLineBrush, 3);
                }
                else
                {
                    RenderTarget2D.DrawLine(point1, point2, greenLineBrush, 1);
                    RenderTarget2D.DrawLine(point3, point4, greenLineBrush, 3);
                }
            }
        }
        private void DrawAll(int startCount)
        {
            DrawStock(startCount);
            DrawMa(20, Color.LightGreen);
            DrawMa(60, Color.Red);
            DrawVolume();
            DrawBoll();
        }
        private void Clear()
        {
            if (Base.CurrentIndexCode != indexCodeList[comboBox_code.SelectedIndex])
            {
                Base.CurrentIndexCode = "";
                indexData = null;
                indicators = null;
                maxCount = 0;
            }
        }

        private void GraphicsClear()
        {
            RenderTarget2D.Clear(Color.DarkGray);
            RenderTarget2D.DrawRectangle(new RawRectangleF(2, 2, renderControl.Width - 2, (int)(renderControl.Height * stockPersent)), blackLineBrush);
            RenderTarget2D.DrawRectangle(new RawRectangleF(2, (int)(renderControl.Height * stockPersent + 5), renderControl.Width - 2, (int)(renderControl.Height * (stockPersent + volumePersent) + 5)), blackLineBrush);
            RenderTarget2D.DrawRectangle(new RawRectangleF(2, (int)(renderControl.Height * (stockPersent + volumePersent) + 10), renderControl.Width - 2, renderControl.Size.Height - 5), blackLineBrush);
        }
        private async Task<bool> RunOnceAsync(string code, int n)
        {
            Base.CurrentIndexCode = code;
            await foreach (string str in GetIndexHistory.UpdateDayAsync(code))
            {
                label_Msg.Text = str;
            }
            label_Msg.Text = "";

            indexData = Base.IndexDatabase.GetIndexAsync().Result;

            indicators = null;
            CalcIndicator.CalcAllMaForCode(indexData, out indicators);
            CalcIndicator.CalcBoll(n, indexData, ref indicators);
            return true;
        }
        private async void Button_find_ClickAsync(object sender, EventArgs e)
        {
            if (indexCodeList[comboBox_code.SelectedIndex].Length != 6)
            {
                comboBox_code.BackColor = System.Drawing.Color.Red;
                return;
            }
            Clear();
            if (!int.TryParse(textBox_variable.Text, out int n))
            {
                return;
            }
            if (await RunOnceAsync(indexCodeList[comboBox_code.SelectedIndex], n))
            {
                showStartCount = oldShowStartCount = indexData.Count;
                renderControl.Refresh();
            }
            else
            {
                label_Msg.Text = "输入错误";
            }
        }
        private void Button_goto_Click(object sender, EventArgs e)
        {

            if (textBox_Date.Text.Length != 8)
            {
                textBox_Date.BackColor = System.Drawing.Color.Red;
                textBox_Date.Text = "长度错误";
                return;
            }
            else
            {
                textBox_Date.BackColor = System.Drawing.Color.White;
            }
            int.TryParse(textBox_Date.Text, out int date);
            int count = 0;
            foreach (var dayData in indexData)
            {
                if (dayData.Date >= date || count >= indexData.Count - 20)
                {
                    Clear();
                    showStartCount = oldShowStartCount = count;
                    renderControl.Refresh();

                    return;
                }
                count++;
            }

            //DateTime date = new DateTime(), textBox1.Text.Take(4), textBox1.Text.Take(4));
        }
        private void Button_Clear_Click(object sender, EventArgs e)
        {
            comboBox_code.SelectedItem = 0;
            Clear();
        }
        private void Button_Strategy_Click(object sender, EventArgs e)
        {
            inFocus = false;
            Form3 form3 = new Form3();
            form3.Show();
        }

        private void button_log_Click(object sender, EventArgs e)
        {
            TextRecord.OpenRecord();
        }

        private void Form5_FormClosing(object sender, FormClosingEventArgs e)
        {

        }
        private void Form5_Resize(object sender, EventArgs e)
        {
            SetWindowSize();
        }
        void SetWindowSize()
        {
            renderControl.Top = 0;
            renderControl.Height = this.Height - 75;
            renderControl.Left = 0;
            renderControl.Width = this.Width - 20;
            showCount = (int)Math.Floor((renderControl.Width) / 5f -2);
        }

        private void renderControl_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                Cursor = Cursors.Hand;

                if (LMouseDown == false)
                {
                    mouseDownPoint = new RawVector2(-e.X, -e.Y);
                    LMouseDown = true;
                    oldShowStartCount = showStartCount;
                    return;
                }
            }
        }

        private void renderControl_MouseMove(object sender, MouseEventArgs e)
        {
            mousePos = new RawVector2(e.X, e.Y);
            if (!inFocus) return;
            if (LMouseDown)
            {
                showStartCount = (int)(oldShowStartCount + (-e.X - mouseDownPoint.X) * 0.2);
                if (showStartCount < 0) showStartCount = 0;
                if (showStartCount != oldShowStartCount)
                {

                    label_Msg.Text = showStartCount.ToString();
                    Clear();
                    renderControl.Refresh();
                }
            }
            else
            {
                if (indexData == null || indicators == null)
                {
                    return;
                }

                if (renderControl.Width > e.X & 0 < e.X & renderControl.Height - 30 > e.Y & 0 < e.Y)
                {
                    int x = (int)(mousePos.X / 5) - 1;
                    if (lineX != x)
                    {
                        lineX = x;
                        int dateCount = showStartCount + x;
                        if (dateCount < 0 || dateCount >= maxCount) return;
                        Index dayData = indexData[dateCount];
                        Indicator dayIndicator = indicators[dateCount];
                        string text = string.Format("代码:{0}\n" +
                            "名称:{1}\n" +
                            "日期:{2}\n" +
                            "开盘:{3}\n" +
                            "收盘:{4}\n" +
                            "最高:{5}\n" +
                            "最低:{6}\n" +
                            "手数:{7}\n" +
                            "换手率:{8}%\n" +
                            "成交额:{9}\n" +
                            "20日均线:{10}\n" +
                            "布林上轨:{11}\n" +
                            "布林中轨:{12}\n" +
                            "布林下轨:{13}\n",
                            Base.CurrentIndexCode,
                            comboBox_code.Text,
                            dayData.Date.ToString(),
                            dayData.OpenPrice,
                            dayData.ClosePrice,
                            dayData.HighPrice,
                            dayData.LowPrice,
                            dayData.TotalHand,
                            dayData.HandRate,
                            dayData.TotalAmount,
                            dayIndicator.Ma20.ToString("F2"),
                            dayIndicator.BollUp.ToString("F2"),
                            dayIndicator.BollMd.ToString("F2"),
                            dayIndicator.BollDown.ToString("F2"));
                        showText = text;
                        renderControl.Refresh();
                    }
                }
            }
        }

        private void renderControl_MouseUp(object sender, MouseEventArgs e)
        {
            LMouseDown = false;
            Cursor = Cursors.Default;
            oldShowStartCount = showStartCount;

        }

    }
}
